package com.briup.pai.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.briup.pai.common.constant.LoginConstant;
import com.briup.pai.common.enums.ResultCodeEnum;
import com.briup.pai.common.enums.UserStatusEnum;
import com.briup.pai.common.exception.BriupAssert;
import com.briup.pai.common.utils.JwtUtil;
import com.briup.pai.common.utils.MessageUtil;
import com.briup.pai.common.utils.RedisUtil;
import com.briup.pai.common.utils.SecurityUtil;
import com.briup.pai.convert.UserConvert;
import com.briup.pai.dao.UserMapper;
import com.briup.pai.entity.dto.LoginWithPhoneDTO;
import com.briup.pai.entity.dto.LoginWithUsernameDTO;
import com.briup.pai.entity.po.User;
import com.briup.pai.entity.vo.CurrentLoginUserVO;
import com.briup.pai.service.IAuthService;
import com.briup.pai.service.ILoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserServiceImpl userServiceImpl;
    @Autowired
    private IAuthService authService;
    @Autowired
    private UserConvert userConvert;
//    @Autowired
//    private BaseConvert baseConvert;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public String loginWithUsername(LoginWithUsernameDTO dto) {

        // 登陆逻辑
        // 获取username和password进行查询
        String username = dto.getUsername();
        String password = dto.getPassword();

        // 根据用户名字查出来的user对象，username必须存在
        User user = BriupAssert.requireNotNull(userServiceImpl,
                User::getUsername,
                username,
                ResultCodeEnum.USER_NOT_EXIST);
        // password必须正确
        BriupAssert.requireEqual(user.getPassword(),
                                 // 密码进行md5加密和查出来的密码进行比对
                                 DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)),
                                 ResultCodeEnum.PASSWORD_IS_WRONG);
        // 账号状态必须可用
        BriupAssert.requireEqual(user.getStatus(),
                                 UserStatusEnum.AVAILABLE.getStatus(),  // 0
                                 ResultCodeEnum.USER_IS_DISABLED);

        // 将user信息(id)转换为JWT字符串返回
        HashMap<String, Object> map = new HashMap<>();
        map.put(LoginConstant.JWT_PAYLOAD_KEY, user.getId());
        return JwtUtil.generateJwt(map);
    }

    @Override
    public CurrentLoginUserVO getCurrentUser() {
        Integer userId = SecurityUtil.getUserId();
        // 从TheadLocal中获取当前登录用户id
        CurrentLoginUserVO currentLoginUserVO = userConvert.po2CurrentLoginUserVO(
                userMapper.selectById(userId));
        // ======= 实现了动态路由之后 ，添加下面的代码，获取用户的菜单和按钮权限
        currentLoginUserVO.setMenu(authService.getRouter(userId));
        currentLoginUserVO.setButtons(authService.getUserButtonPermissionList(userId));

        return currentLoginUserVO;
    }

    @Override
    public void sendMessageCode(String telephone) {
        // 发送手机短信，接收验证码，将验证码保存到redis中
        // 1、准备保存到redis中的key和time
        String key = LoginConstant.USER_SMS_VERIFY_CODE_PREFIX + telephone;
        long time = LoginConstant.USER_SMS_VERIFY_CODE_EXPIRATION_TIME;
        // 验证一下用户、用户状态、验证码是否重复（判断key是不是在redis中）
        User user = BriupAssert.requireNotNull(userServiceImpl,
                                               User::getTelephone,
                                               telephone,
                                               ResultCodeEnum.USER_NOT_EXIST);
        BriupAssert.requireEqual(user.getStatus(),
                                 UserStatusEnum.AVAILABLE.getStatus(),
                                 ResultCodeEnum.USER_IS_DISABLED);
        BriupAssert.requireFalse(redisUtil.exist(key),
                                ResultCodeEnum.USER_VERIFY_CODE_ALREADY_EXIST);
        // 2、生成验证码，使用hutools里的工具
        Integer value = RandomUtil.randomInt(1000,9999);
        try {
            MessageUtil.sendMessage(telephone, 1234);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        redisUtil.set(key, value, time);
    }

    @Override
    public String loginWithTelephone(LoginWithPhoneDTO dto) {
        // 验证手机号和redis中的验证码是否匹配
        // 获取手机号
        String telephone = dto.getTelephone();
        // 获取验证码
        Integer code = dto.getCode();
        // 验证telephone的对象
        User user = BriupAssert.requireNotNull(userServiceImpl,
                User::getTelephone,
                telephone,
                ResultCodeEnum.USER_NOT_EXIST);
        // 验证user的状态必须可用
        BriupAssert.requireEqual(user.getStatus(),
                UserStatusEnum.AVAILABLE.getStatus(),
                ResultCodeEnum.USER_IS_DISABLED);
        // 准备reids中的key
        String key = LoginConstant.USER_SMS_VERIFY_CODE_PREFIX + telephone;
        // 确保key在redis中必须存在
        BriupAssert.requireTrue(redisUtil.exist(key),
                ResultCodeEnum.USER_VERIFY_CODE_NOT_EXIST);
        // 和redis中的key的value进行对比
        Integer value = (Integer) redisUtil.get(key);
        // 验证value和code
        BriupAssert.requireEqual(value, code, ResultCodeEnum.USER_VERIFY_CODE_ERROR);
        // 登陆成功之后，删除redis中的验证码
        redisUtil.remove(key);
        // 返回jwt字符串
        Map<String,Object> map =  new HashMap<>();
        map.put(LoginConstant.JWT_PAYLOAD_KEY, user.getId());
        return JwtUtil.generateJwt(map);
    }
}